/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 *
 */

import { OhosAppTask } from './task/ohos-app-task';
import {FileSet, Project, TaskInputValue} from '@ohos/hvigor';
import path from 'path';
import { ProjectTaskService } from './service/project-task-service';
import { TaskNames } from './common/task-names';
import Task = TaskNames.Task;
import {
    checkPacJsonExistAndGetObj,
    mergeDependencies,
    mergePacFiles, PacFile, pacFileValidate,
    PackageJsonWithOtherInfo,
    tracePac
} from '../utils/pac-util';
import { CommonConst } from '../const/common-const';
import fse from 'fs-extra';
import { MakeProjectPackInfo } from './make-project-pack-info';
import { OhosPluginId } from "../plugin/common/ohos-plugin-id";
import { ModuleTaskService } from "./service/module-task-service";
import { AbstractHapModulePlugin } from "../plugin/common/abstract-hap-module-plugin";
import { HspPlugin } from "../plugin/hsp-plugin";
import { getJson5Obj } from "../utils/json-util";
import { PackageJson } from 'type-fest';

export class ProcessProjectPrivacyProfile extends OhosAppTask {
    private outputPath = path.resolve(this.service.getPathInfo().getProjectOutputPath(), CommonConst.PAC_ONLY_JSON);
    private appPacJson = path.resolve(this.service.getProjectModel().getProjectDir(), CommonConst.APP_SCOPE, CommonConst.PAC_JSON);
    constructor(project: Project, taskService: ProjectTaskService) {
        super(project, taskService, Task.PROCESS_PROJECT_PRIVACY_PROFILE);
    }

    declareInputs(): Map<string, TaskInputValue> {
        const map = super.declareInputs();
        this.project.getAllSubModules().forEach((module) => {
            const modulePackageJsonObj = getJson5Obj(path.resolve(module.getNodeDir(), CommonConst.OH_PACKAGE_JSON5));
            map.set(`${module.getName()}dependencies`, JSON.stringify(modulePackageJsonObj?.dependencies) ?? '');
            map.set(`${module.getName()}devDependencies`, JSON.stringify(modulePackageJsonObj?.devDependencies) ?? '');
            map.set(`${module.getName()}dynamicDependencies`, JSON.stringify(modulePackageJsonObj?.dynamicDependencies) ?? '');
        })
        const projectPackageJsonPbj = getJson5Obj(path.resolve(this.project.getNodeDir(), CommonConst.OH_PACKAGE_JSON5));
        map.set('dependencies', JSON.stringify(projectPackageJsonPbj?.dependencies) ?? '');
        map.set('devDependencies', JSON.stringify(projectPackageJsonPbj?.devDependencies) ?? '');
        map.set('dynamicDependencies', JSON.stringify(projectPackageJsonPbj?.dynamicDependencies) ?? '');
        return map;
    }

    declareInputFiles(): FileSet {
        const files = super.declareInputFiles();
        if (fse.existsSync(this.appPacJson)) {
            files.addEntry(this.appPacJson);
        }
        this.project.getAllSubModules().forEach((module) => {
            const modulePacPath = path.resolve(module.getNodeDir(), CommonConst.PAC_JSON);
            if (module.getPluginById(OhosPluginId.OHOS_HAP_PLUGIN) || !fse.existsSync(modulePacPath)) {
                return;
            }
            if (fse.existsSync(modulePacPath)) {
                files.addEntry(modulePacPath);
            }
        });
        return files;
    }

    declareOutputFiles(): FileSet {
        const outPutFiles = new FileSet();
        outPutFiles.addEntry(this.outputPath);
        return outPutFiles;
    }

    // 原则上依赖于pre-build中的校验任务，用这个作为依赖可以确保产物目录一定存在。
    protected initTaskDepends() {
        this.dependsOn(MakeProjectPackInfo.name);
    }

    protected doTaskAction() {
        // 检查schema路径是否存在
        if (!fse.existsSync(this.service.getSdkInfo().getPacSchema())) {
            return;
        }
        // 1. 收集项目appScope下的pac文件。HSP的pac文件，被依赖的本地har模块的pac文件，以及工程级、工程所有hap、hsp模块的依赖的pac文件
        // 1.1 收集项目appScope下的pac文件
        const pacFiles: string[] = [];
        // 1.1.1 收集项目所有三方包的依赖信息，复制到pac文件中，去重
        const ohPackageObjs: PackageJson[] = [];
        this.service.getAllOtherDependencies().forEach((value) => {
            ohPackageObjs.push(value.getPackageJsonObj());
        });
        if (pacFileValidate(this.project.getName(), this.appPacJson, this.service.getSdkInfo().getPacSchema())) {
            pacFiles.push(this.appPacJson);
        }
        // 1.2 收集工程级依赖的pac文件
        this.service.getAllModuleOrProjectDependencies().forEach((value) => {
            const pacFilePath = path.resolve(value.getDependencyRootPath(), CommonConst.PAC_JSON);
            if (pacFileValidate(this.project.getName(), pacFilePath, this.service.getSdkInfo().getPacSchema())) {
                pacFiles.push(pacFilePath);
            }
        });
        // 1.2 收集源码模块以及其依赖的pac文件
        this.project.getAllSubModules().forEach((module) => {
            const plugin = module.getPluginById(OhosPluginId.OHOS_HAP_PLUGIN) ?? module.getPluginById(OhosPluginId.OHOS_HSP_PLUGIN);
            if (plugin !== undefined && (plugin instanceof AbstractHapModulePlugin || plugin instanceof HspPlugin) && plugin.getTaskService()) {
                const moduleTargetService = plugin.getTaskService() as ModuleTaskService;
                // 1.2.1 取本模块的根目录下的pac文件
                if (!module.getPluginById(OhosPluginId.OHOS_HAP_PLUGIN)) {
                    const modulePacPath = path.resolve(module.getNodeDir(), CommonConst.PAC_JSON);
                    if (pacFileValidate(this.project.getName(), modulePacPath, this.service.getSdkInfo().getPacSchema())) {
                        pacFiles.push(modulePacPath);
                    }
                }
                // 1.2.2 取模块依赖的根目录下的pac文件，并校验
                moduleTargetService.getAllModuleOrProjectDependencies().forEach((value) => {
                    const pacFilePath = path.resolve(value.getDependencyRootPath(), CommonConst.PAC_JSON);
                    if (pacFileValidate(this.project.getName(), pacFilePath, this.service.getSdkInfo().getPacSchema())) {
                        pacFiles.push(pacFilePath);
                    }
                });
                // 收集Hsp和Hap的所有三方包的oh-package的信息
                moduleTargetService.getAllOtherDependencies().forEach((value) => {
                    ohPackageObjs.push(value.getPackageJsonObj());
                });
            }
        })
        // 2. 检查pac文件是否存在，生成pac对象
        const pacObjs = pacFiles.map((pacPath) => checkPacJsonExistAndGetObj(pacPath))
            .filter((obj) : obj is PacFile => { // 这个写法为了让类型检查器明白已经数组不存在undefined
                return obj !== undefined; // 这里为了去掉数组中的undefined对象
            });
        // 3. 对pac文件合并去重
        const mergedPacJsonObj = mergePacFiles(pacObjs);
        // 将所有三方依赖中的oh-package.json的author、name、description、version放到合并后的pac.json文件的dependencies
        mergeDependencies(ohPackageObjs as PackageJsonWithOtherInfo[], mergedPacJsonObj);
        // 4. pac文件路径先放置到产物路径中
        fse.outputJSONSync(this.outputPath, mergedPacJsonObj);
        // 5. 打点
        tracePac(this.project.getName());
    }
}